#!/usr/bin/perl

use strict;

use LWP::UserAgent;
use HTTP::Request::Common;
use MIME::Base64;
use XML::Simple;
use Data::Dumper;
use Getopt::Long;

local $SIG{__WARN__} = sub { };

my $server;
my $organization;
my $user;
my $orgvdc;
my $catalog;
my $vapptemplate;
my $vappname;
my $vappnetwork;
my $debug;

if ( @ARGV > 0 ) {
  GetOptions('s|server=s' => \$server,
             'o|organization=s' => \$organization,
             'u|user=s' => \$user,
             'v|orgvdc=s' => \$orgvdc,
             'c|catalog=s' => \$catalog,
             't|vapptemplate=s' => \$vapptemplate,
             'n|vappname=s' => \$vappname,
             'w|vappnetwork=s' => \$vappnetwork,
             'd|debug' => \$debug);
}
unless ($server && $organization && $user && $orgvdc && $vappname) {
  print "Usage: create.pl --server SERVER --organization ORGANIZATION --user USER --orgvdc ORGVDC --catalog CATALOG --vapptemplate VAPPTEMPLATE --vappname VAPPNAME --vappnetwork VAPPNETWORK [--debug]\n";
  print "...or  create.pl -s SERVER -o ORGANIZATION -u USER -v ORGVDC -c CATALOG -t VAPPTEMPLATE -n VAPPNAME -k VAPPNETWORK [-d]\n";
  exit;
}

my $version = "v1.0";
my $vcdurl = "https://" . $server . "/api/$version";
my $vcdpass;
my $trk_loaded = 0;
# "use" loads modules at compile-time. Since Term::ReadKey is used
# if it is installed, but there's no problem if not, we "require"
# instead and import their symbols by hand if successful.
#
# See perldoc -f require and perldoc -q require for further details.
eval {
    require Term::ReadKey;
    import Term::ReadKey;
    $trk_loaded = 1;
};
print 'vDC password: ';
ReadMode('noecho') if $trk_loaded;  # input echo off
chomp($vcdpass = $trk_loaded ? ReadLine(0) : <STDIN>);
ReadMode('restore') if $trk_loaded; # input echo on
print "\n";

my $xml;
my $response;

# create browser with cookies enabled in memory only
# cookies are required because the vCloud Director server issues a 
# session cookie which must be submitted with every subsequent request

my $userAgent = LWP::UserAgent->new(agent => 'perl LWP', cookie_jar => {});

# login with the supplied credentials

my $cryptAuth = encode_base64($user . "@" . $organization . ":" . $vcdpass);

$response = $userAgent->request(POST $vcdurl . '/login',
                        Content_Type => 'text/xml',
                        Authorization => 'Basic ' . $cryptAuth);

unless (  $response->is_success ) {
  die $response->as_string;
}

if ( $debug ) {
  print "login successful.\n";
  print $response->content;
}

# browse the organization to find the Catalog and OrgVDC

$xml = XMLin($response->content, KeyAttr => [ ], ForceArray => 1);

my $orgRef = "";
my $vdcRef = "";
my $catRef = "";

#my $orgRef = $xml->{Org}->{href};
foreach my $org (@{$xml->{Org}}) {
  if ( $org->{name} eq $organization) {
      $orgRef = $org->{href};
  }
}

if ($orgRef) {
  print "$orgRef\n" if ($debug);
}
else {
  die "$organization not found!\n";
}

$response = $userAgent->request(GET $orgRef);

unless (  $response->is_success ) {
  die $response->as_string;
}

if ( $debug ) {
  print $response->content;
}

$xml = XMLin($response->content, KeyAttr => [ ], ForceArray => 1);

foreach my $link (@{$xml->{Link}}) {
  if ( $link->{type} eq 'application/vnd.vmware.vcloud.vdc+xml' ) {
    if ( $link->{name} eq $orgvdc) {
      $vdcRef = $link->{href};
    }
  }
  if ( $link->{type} eq 'application/vnd.vmware.vcloud.catalog+xml' ) {
    if ( $link->{name} eq $catalog) {
      $catRef = $link->{href};
    }
  }
}

unless ( $vdcRef ne "" ) {
  die ("OrgVDC $orgvdc not found!\n");
}

unless ( $catRef ne "" ) {
  die ("Catalog $catalog not found!\n");
}

if ( $debug ) { print "OrgVDC: $vdcRef\n"; }
if ( $debug ) { print "Catalog: $catRef\n"; }

# find the vApp Catalog Item

my $vappCatalogItem;

$response = $userAgent->request(GET $catRef);

unless (  $response->is_success ) {
  die $response->as_string;
}

if ( $debug ) {
  print $response->content;
}

$xml = XMLin($response->content);

if ( defined $xml->{CatalogItems}->{CatalogItem}->{$vapptemplate}->{href} ) {

  $vappCatalogItem = $xml->{CatalogItems}->{CatalogItem}->{$vapptemplate}->{href};
} else {
  die ("vApp Template $vapptemplate not found!\n");
}

if ( $debug ) { print "vappCatalogItem:$vappCatalogItem\n"; }

# get the vAppTemplateUri

my $vapptemplateUri;

$response = $userAgent->request(GET $vappCatalogItem);

unless (  $response->is_success ) {
  die $response->as_string;
}
if ( $debug ) {
  print $response->content;
}

$xml = XMLin($response->content);

$vapptemplateUri = $xml->{Entity}->{href};

if ( $debug ) { print "vappTemplateUri:$vapptemplateUri\n"; }

# get the parentNetRef

$response = $userAgent->request(GET $vdcRef);

unless (  $response->is_success ) {
  die $response->as_string;
}
if ( $debug ) {
  print $response->content;
}

$xml = XMLin($response->content);

my $parentNetRef = "";

if ( defined $xml->{AvailableNetworks}->{Network}->{$vappnetwork}->{href} ) {
  $parentNetRef = $xml->{AvailableNetworks}->{Network}->{$vappnetwork}->{href};
} else {
  die ("vApp Network $vappnetwork not found!\n");
}

if ( $debug ) { print "parentNetRef:$parentNetRef\n"; }

# make sure we have a unique vApp name

if ( defined $xml->{ResourceEntities}->{ResourceEntity}->{$vappname} ) {
  if ( $xml->{ResourceEntities}->{ResourceEntity}->{$vappname}->{type} eq "application/vnd.vmware.vcloud.vApp+xml" ) {
    my $tryagain = 1;
    my $uid =0;
    my $temp = "";
    while ($tryagain) {
      $uid++;
      $temp = $vappname . $uid;
      if (! defined $xml->{ResourceEntities}->{ResourceEntity}->{$temp} ) {
        $tryagain = 0;
        $vappname = $temp;
      }
    }
  }
}



# instantiate the vApp from the vApp Template URI

# specify network(s), guest customization, etc...

my $instantiationParams = "<NetworkConfigSection>
<ovf:Info>Configuration parameters for vAppNetwork</ovf:Info>\n
<NetworkConfig networkName=\"$vappnetwork\">\n 
<Configuration>
<ParentNetwork href=\"$parentNetRef\"/>
<FenceMode>bridged</FenceMode>
</Configuration>\n
</NetworkConfig>\n
</NetworkConfigSection>";

my $message = "<InstantiateVAppTemplateParams name=\"$vappname\" deploy=\"false\" powerOn=\"false\" xmlns=\"http://www.vmware.com/vcloud/v1\"
xmlns:ovf=\"http://schemas.dmtf.org/ovf/envelope/1\" >
<Description>API Test VApp</Description>
<InstantiationParams>
$instantiationParams
</InstantiationParams>
<Source href=\"$vapptemplateUri\"/>
</InstantiateVAppTemplateParams>";

if ($debug) { print "POST " . $vdcRef . "/action/instantiateVAppTemplate\n"; }
if ($debug) { print $message; }

$response = $userAgent->request(POST $vdcRef . '/action/instantiateVAppTemplate',
                        Content_Type => 'application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml',
                        Content => $message);

unless (  $response->is_success ) {
  die $response->as_string;
}
if ( $debug ) {
  print $response->content;
}

# get the new vApp URI, task and task status then wait for completion

$xml = XMLin($response->content);

my $newVappUri = $xml->{Tasks}->{Task}->{Owner}->{href};

if ( $debug ) { print "newVappUri:$newVappUri\n"; }

my $taskStatus = $xml->{Tasks}->{Task}->{status};
my $taskRef = $xml->{Tasks}->{Task}->{href};

local $| = 1;  # set unbuffered output

print "Creating vApp";

while ( $taskStatus eq "running" ) {

  print ".";
  sleep (5);

  $response = $userAgent->request(GET $taskRef);
  $xml = XMLin($response->content);
  unless (  $response->is_success ) {
    die $response->as_string;
  }

  $taskStatus = $xml->{status};
  
}

unless ( $taskStatus eq "success" ) {
  die $response->as_string;
}

print "\nvApp creation complete.\n";

# logout

$response = $userAgent->request(POST $vcdurl . '/logout');

if ( $debug ) {
  print "POST ".$vcdurl."/logout\n";
  print $response->content;
}
unless (  $response->is_success ) {
  die $response->as_string;
}

if ( $debug ) { print "logout successful.\n"; }

__END__

$xml = XMLin($response->content);
$Data::Dumper::Sortkeys = 1; #Sort the keys in the output
$Data::Dumper::Deepcopy = 1; #Enable deep copies of structures
$Data::Dumper::Indent = 1;   #Enable enough indentation to read the output
print Dumper($xml);
